Skip to content

Validate generated server replies with explicit status codes - #129

Merged
tanmaykm merged 2 commits into
JuliaComputing:mainfrom
quinnj:feat/explicit-server-replies
Sep 28, 2026
Merged

tanmaykm merged 2 commits into
JuliaComputing:mainfrom
quinnj:feat/explicit-server-replies

Conversation

@quinnj

@quinnj quinnj commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

Generated server handlers can now return OpenAPI.Reply(status, body) to choose a documented status while retaining schema validation and encoding. This supports distinct typed 200/202 results, typed errors, and multiple statuses sharing one body type. Exact status takes precedence over a range and then default; undocumented statuses fail with a clear operation/status error. Plain results and raw HTTP.Response returns retain their existing behavior.

Compatibility: newly generated clients and servers target contract 4. The runtime accepts contracts 3 through 4, so stored contract-3 modules continue to load and retain their behavior. Regenerate servers when adopting Reply. Contract-4 modules require OpenAPI 1.2 or later; older runtimes still reject them at load time. Packages committing contract-4 generated output should declare OpenAPI = "1.2" compatibility.

Reply accepts final HTTP statuses 200–599 in both constructor forms. It adds no exports, dependencies, or header overrides. Generated handler comments now describe the return behavior instead of advertising a misleading union of every documented body type. A schema named Reply still works because the wrapper is referenced through OpenAPI.Reply.

Validation:

  • Full package suites pass on Julia 1.10.12, 1.12.7, and 1.13.0.
  • Live generated-client/server tests cover exact/range/default selection, schema errors, undocumented statuses, plain/raw returns, null/empty/text/binary/sequential bodies, and generated-name collisions. The focused checks also pass with JSON 1.7.1.
  • All four pinned Petstore, Discord, Stripe, and GitHub corpus cases pass; strict documentation builds pass.
  • Runtime-input Reply construction passes actual JuliaC --trim=safe compile/run on 1.12 and 1.13 with runtime code generation disabled. A separate native selector control passes exact/range/default cases. Full generated response encoding remains outside this proof: the same bounded fixture reports 153 verifier errors on unchanged main, patched plain returns, and patched explicit replies.

Validation for the compatibility update: full Julia 1.10.12 and 1.12.7 suites, the small pinned corpus, native trim checks, and the strict documentation build pass locally. Contract-guard regressions cover acceptance of 3 and 4 and rejection of 1, 2, and 5. Client/server files generated from the pre-PR contract-3 code complete a live HTTP round trip on the new runtime. The actual pre-PR runtime rejects both contract-4 client and server files with its existing regeneration error.

All four hosted CI checks pass on commit 4209477: Julia 1.10, current Julia, documentation, and the OpenAPI corpus.

Fixes #119.

Co-authored by Codex

@tanmaykm

Copy link
Copy Markdown
Member

Thanks, the Reply design looks good to me. One suggestion about the contract bump before this merges.

As written, the new runtime accepts only contract 4, so every stored contract-3 module fails at load, including clients and servers that never touch Reply. Downstream packages that commit generated modules and declare OpenAPI = "1" compat (the generated-package pattern we use internally) are hit hardest. The resolver will happily pick 1.2 on the next Pkg.update, and the failure only shows up when the service starts. That forces each environment to regenerate and bump OpenAPI in the same change.

As far as I can tell, contract-3 code runs unchanged on this runtime. The runtime.jl diff only adds Reply: no imported name is removed or renamed, no Spec or descriptor shape changes, and _select_response already exists on main. _server_response is emitted into each generated module, so older servers keep their old behaviour. The bump is only needed for the opposite case, a contract-4 module on an older runtime, and the current exact check in 1.1.x already rejects that with a clear regeneration message.

So could require_contract accept a supported range instead of an exact version?

const CONTRACT_VERSION = 4
# Oldest generated-code contract this runtime still serves. Raise it when a
# change removes or alters something older generated modules depend on.
const MIN_CONTRACT_VERSION = 3

function require_contract(version::Integer, generator::AbstractString)
    MIN_CONTRACT_VERSION <= version <= CONTRACT_VERSION && return nothing
    # ... existing error, perhaps naming the supported range
end

What that gets us:

  • Existing contract-3 clients and servers keep loading on 1.2. Users regenerate when they want Reply, not because they upgraded.
  • A contract-4 module on OpenAPI ≤ 1.1.x still fails loudly, because older runtimes check for an exact match.
  • Future additive bumps get the same treatment, and MIN_CONTRACT_VERSION only moves for real breaks.

Test and doc changes this would need:

  • test/generated_contract.jl: expect contracts 1 and 2 and current + 1 to be rejected, and 3 and 4 to be accepted. Turn the "stored contract-3 modules fail" block into one that loads a contract-3 client and server successfully.
  • Drop the "regenerate all stored clients and servers" notes from docs/src/artifacts.md and docs/src/servers.md. Instead, say that generated modules need an OpenAPI version whose runtime provides their contract, and that modules using OpenAPI.Reply need 1.2 or later.

Downstream generators that commit their output can then set a compat floor from the contract they emit (contract 4 → OpenAPI = "1.2"). The resolver catches the new-module-on-old-runtime case, and nothing has to move in lock-step.

Happy to push these changes to the branch if that's easier.

@quinnj

quinnj commented Sep 28, 2026

Copy link
Copy Markdown
Contributor Author

I pushed the updates. Thanks.

@tanmaykm

Copy link
Copy Markdown
Member

Thanks! Merging it now.

@tanmaykm
tanmaykm merged commit 57a1852 into JuliaComputing:main Sep 28, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Deal with different success responses

2 participants